package com.example.base.Redis;

import com.alibaba.fastjson.JSON;
import com.example.base.Cache.Cache;
import com.example.base.Cache.CacheKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author wangsaichao
 * 基于spring和redis的mredisTemplate工具类
 */
public class RedisAccessManager implements Cache {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Value("${dev.prefix}")
    private  String CONTEXT=null ;
    public static final String SEPERATOR = ":";
    public static final String DEFAULT_FIELD = "DF";
    private Integer ex = 60 * 60 * 1000;
    public Object hGet(final String key, final String field) {
        return   redisTemplate.opsForHash().get(getKey(key),field);
    }

    public Object hGet(final String key, final String extKey, final String field) {
        return   redisTemplate.opsForHash().get(getKey(key, extKey),field);
    }

    public Object hGet(final Class<?> clazz, final String field) {
        return   redisTemplate.opsForHash().get(getKey(clazz),field);
    }

    public Object hGet(final Class<?> clazz, final String extKey, final String field) {
        return   redisTemplate.opsForHash().get(getKey(clazz, extKey),field);
    }

    public void set(final CacheKey obj) {
        redisTemplate.opsForValue().set(getKey(obj.getClass()),obj);
    }



    public void set(final String key, Object obj, final int ex) {
        redisTemplate.opsForValue().set(key,obj,ex,TimeUnit.SECONDS);
    }

    public void hSet(final String key, final String field, Object obj) {
        redisTemplate.opsForHash().put(key,field,obj);
    }

    public void hSet(final CacheKey obj, final String extKey, final String field) {
        redisTemplate.opsForHash().put(getKey(obj.getClass(), extKey),field,obj);
    }

    @Override
    public void del(final CacheKey o) {
       Boolean res= redisTemplate.delete(getKey(o.getClass()));
        if(res)
            System.out.println("redis delete(final CacheKey o) source :"+o.getClass().getSimpleName()+ " key: "+getKey(o.getClass())+"successfully");
        else
            System.out.println("redis delete(final CacheKey o) source :"+o.getClass().getSimpleName()+ " key: "+getKey(o.getClass())+"failed");
    }

    @Override
    public void del(final String key) {
        Boolean res=redisTemplate.delete(key);
        if(res)
            System.out.println("redis delete(final String key) key:"+key +"successfully");
        else
            System.out.println("redis delete(final String key) key:"+key +"failed");

    }

    @Override
    public void del(final Class<? extends CacheKey> clazz) {
       Boolean res= redisTemplate.delete(getKey(clazz));
        if(res)
            System.out.println("redis delete (final Class<? extends CacheKey> clazz) source :"+clazz.getSimpleName()+ " key: "+getKey(clazz)+"successfully");
        else
            System.out.println("redis delete(final Class<? extends CacheKey> clazz) source :"+clazz.getSimpleName()+ " key: "+getKey(clazz)+"failed");
    }

    @Override
    public void del(final Class<? extends CacheKey> clazz, final String extKey) {
       Boolean res= redisTemplate.delete(getKey(clazz, extKey));
        if(res)
            System.out.println("redis delete(final Class<? extends CacheKey> clazz, final String extKey)  source :"+clazz.getSimpleName()+ " key: "+getKey(clazz, extKey)+"successfully");
        else
            System.out.println("redis delete(final Class<? extends CacheKey> clazz, final String extKey)  source :"+clazz.getSimpleName()+ " key: "+getKey(clazz, extKey)+"failed");

    }

    @Override
    public void hDel(final Class<? extends CacheKey> clazz, final String field) {
        Long res=redisTemplate.opsForHash().delete(getKey(clazz),field);
        if(res>0)
            System.out.println("redis hDel(final Class<? extends CacheKey> clazz, final String field) source :"+clazz.getSimpleName()+ " key: "+getKey(clazz)+"successfully influence "+res);
        else
            System.out.println("redis hDel(final Class<? extends CacheKey> clazz, final String field) source :"+clazz.getSimpleName()+ " key: "+getKey(clazz)+"failed");
    }

    @Override
    public void hDel(final Class<? extends CacheKey> clazz, final String extKey, final String field) {
        Long res=redisTemplate.opsForHash().delete(getKey(clazz, extKey),field);
        if(res>0)
            System.out.println("redis hDel(final Class<? extends CacheKey> clazz,  final String extKey, final String field) source :"+clazz.getSimpleName()+ " key: "+getKey(clazz, extKey)+"successfully influence "+res);
        else
            System.out.println("redis hDel(final Class<? extends CacheKey> clazz,  final String extKey, final String field) source :"+clazz.getSimpleName()+ " key: "+getKey(clazz, extKey)+"failed");
    }

    private String getKey(String key) {

        StringBuilder sb = new StringBuilder(CONTEXT);
        sb.append(key);
        return sb.toString();
    }

    private String getKey(String key, String extKey) {

        StringBuilder sb = new StringBuilder(CONTEXT);
        sb.append(key).append(SEPERATOR).append(extKey);
        return sb.toString();
    }

    private String getKey(Class<?> clazz) {

        StringBuilder sb = new StringBuilder(CONTEXT);
        sb.append(clazz.getSimpleName()).append(SEPERATOR).append(DEFAULT_FIELD);
        return sb.toString();
    }

    private String getKey(Class<?> clazz, String extKey) {

        StringBuilder sb = new StringBuilder(CONTEXT);
        sb.append(clazz.getSimpleName()).append(SEPERATOR).append(extKey);
        return sb.toString();
    }
    //=============================common============================
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     */
    public void expire(String key,long time){
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String key){
        return redisTemplate.hasKey(key);
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String ... key){
        if(key!=null&&key.length>0){
            if(key.length==1){
                redisTemplate.delete(key[0]);
            }else{
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    /**
     * 批量删除key
     * @param keys
     */
    public void del(Collection keys){
        redisTemplate.delete(keys);
    }


    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key){
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     */
    public void set(String key,Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     */
    public void set(String key,Object value,long time){
        if(time>0){
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
        }else{
            set(key, value);
        }
    }

    /**
     * 使用scan命令 查询某些前缀的key
     * @param key
     * @return
     */
    public Set<String> scan(String key){
         Set<String> execute = this.redisTemplate.execute(new RedisCallback<Set<String>>() {
            @Override
            public Set<String> doInRedis(RedisConnection connection) throws DataAccessException {

                Set<String> binaryKeys = new HashSet<>();

                Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(key).count(1000).build());
                while (cursor.hasNext()) {
                    binaryKeys.add(new String(cursor.next()));
                }
                return binaryKeys;
            }
        });
        return execute;
    }

    /**
     * 使用scan命令 查询某些前缀的key 有多少个
     * 用来获取当前session数量,也就是在线用户
     * @param key
     * @return
     */
    public Long scanSize(String key){
        long dbSize = this.redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                long count = 0L;
                Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(key).count(1000).build());
                while (cursor.hasNext()) {
                    cursor.next();
                    count++;
                }
                return count;
            }
        });
        return dbSize;
    }
}

